#include "server.h"

static struct epoll_event ev, events[MAX_EVENTS];
static modbus_mapping_t * mb_mapping;
static int server_socket;
static int master_scoket;
static int epfd;
static modbus_t *ctx_tcp;
static modbus_t *ctx_rtu;
static char *file_rtu = "/dev/ttyUSB0";
static pthread_mutex_t mutex;
static int auto_switch = 0;
static int debug = 1;

static void close_sigint(int dummy);
static int device_open(void);
static int device_close(void);
static int server_handle(void);
static int server_set_status(const int startaddr, const int nb);
static int server_get_status(const int startaddr, const int nb);
static int server_get_data(const int startaddr, const int nb);
static int server_accept(void);
static void *thread_database(void* t);
static int server_init(void);

//对外接口，用于构建服务器
int server_build(void)
{
	if(!debug)
		daemon(1,0);

	if(debug)
		printf("Enter %s\r\n", __func__);

	int nfds, n;

	//初始化modbus服务器
	if(-1 == server_init()) return -1;

	//添加数据库读线程
	if(-1 == db_init()) return -1;
	pthread_mutex_init(&mutex, NULL);
	pthread_t pid ;
	pthread_create(&pid, NULL, thread_database, NULL);

	while(1)
	{
		nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
		if(-1 == nfds)
		{
			perror("epoll_wait error");
			return -1;
		}
		for(n = 0; n < nfds; ++n)
		{
			master_scoket = events[n].data.fd;
			if(master_scoket == server_socket)
				server_accept();//新连接
			else
				server_handle();//响应请求
		}
	}

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;
}
//初始化modbus服务器
static int server_init(void)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	int rc = -1;

	signal(SIGINT, close_sigint); //信号处理

	ctx_tcp = modbus_new_tcp(TCP_ADDR, TCP_PORT);
	if(NULL == ctx_tcp)
	{
		perror("modbus_new_tcp: ");
		goto NEW_ERROR;
	}

	if(debug) //测试开关
		modbus_set_debug(ctx_tcp, 1);
	modbus_set_response_timeout(ctx_tcp, 10, 0);
	

	server_socket = modbus_tcp_listen(ctx_tcp, NB_CONNECTION); //监听套接字
	if(-1 == server_socket)
	{
		fprintf(stderr, "modbus_tcp_listen: %s", modbus_strerror(errno));
		goto LIS_ERROR;
	}

	mb_mapping = modbus_mapping_new(500, 500, 500, 500); //申请数据存数内存
	if(NULL == mb_mapping)
	{
		fprintf(stderr, "modbus_mapping_new: %s", modbus_strerror(errno));
		goto MAP_ERROR;
	}

	if(debug)
		printf("tab_bits:%p tab_input_bits:%p tab_registers:%p tab_input_registers:%p \r\n", mb_mapping->tab_bits,
				mb_mapping->tab_input_bits, mb_mapping->tab_registers, mb_mapping->tab_input_registers);

	//设置默认保持寄存器
	*(mb_mapping->tab_registers + 0) = 60;
	*(mb_mapping->tab_registers + 1) = 20;
	*(mb_mapping->tab_registers + 2) = 75;
	*(mb_mapping->tab_registers + 3) = 20;
	*(mb_mapping->tab_registers + 4) = 56;
	*(mb_mapping->tab_registers + 5) = 0;
	//初始化epoll相关内容
	epfd = epoll_create(1);
	if(-1 == epfd)
	{
		perror("epoll_create: ");
		goto CRT_ERROR;
	}
	ev.events = EPOLLIN;
	ev.data.fd = server_socket;
	rc = epoll_ctl(epfd, EPOLL_CTL_ADD, server_socket, &ev);
	if(-1 == rc)
	{
		perror("epoll_ctl_add: ");
		goto ADD_ERROR;
	}

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;

	//错误处理
ADD_ERROR:
	close(epfd);
CRT_ERROR:
	modbus_mapping_free(mb_mapping);
MAP_ERROR:
	close(server_socket);
LIS_ERROR:
	modbus_free(ctx_tcp);
NEW_ERROR:
	return -1;
}

static void close_sigint(int dummy)
{
	close(epfd);
	modbus_mapping_free(mb_mapping);
	close(server_socket);
	modbus_free(ctx_tcp);
	pthread_mutex_destroy(&mutex);
	exit(dummy);
}

static int auto_control(uint16_t data[])
{
	int rc = -1;
	uint8_t status[3];
	uint8_t on = 1;
	uint8_t off = 0;

	rc = modbus_read_bits(ctx_rtu, 0, 3, status); //获取当前所有设备的状态
	if(-1 == rc)
	{
		return -1;
	}

	if(data[0]/10 > mb_mapping->tab_registers[0] && status[0] != 0)
	{
		printf("喷淋关闭(led1关)\r\n");
		modbus_write_bits(ctx_rtu, 0, 1, &off);
	}
	if(data[0]/10 < mb_mapping->tab_registers[1] && status[0] == 0)
	{
		printf("喷淋开启(led1\r\n)");
		modbus_write_bits(ctx_rtu, 0, 1, &on);
	}
	if(data[1]/10 > mb_mapping->tab_registers[2] && status[1] == 0)
	{
		printf("风扇开启\r\n");
		modbus_write_bits(ctx_rtu, 1, 1, &on);
	}
	if(data[1]/10 < mb_mapping->tab_registers[3] && status[1] != 0)
	{
		printf("风扇关闭\r\n");
		modbus_write_bits(ctx_rtu, 1, 1, &off);
	}
	if(data[2]/10 > mb_mapping->tab_registers[4] && status[2] != 0)
	{
		printf("光照关闭(led2关)\r\n");
		modbus_write_bits(ctx_rtu, 2, 1, &off);
	}
	if(data[2]/10 < mb_mapping->tab_registers[5] && status[2] == 0)
	{
		printf("光照开启(led2开)\r\n");
		modbus_write_bits(ctx_rtu, 2, 1, &on);
	}

	return 0;
}

static void *thread_database(void* t)
{
	uint16_t data[3] = {0};
	int rc = -1;
	uint8_t test[6] = {0};

	while(1)
	{
		pthread_mutex_lock(&mutex);
		rc = device_open();
		if(-1 ==rc)
		{
			printf("device open error!\r\n");
			device_close();
			pthread_mutex_unlock(&mutex);
			sleep(5);
			continue;
		}

		MODBUS_SET_INT16_TO_INT8(test, 0, data[0]);
		MODBUS_SET_INT16_TO_INT8(test, 2, data[1]);
		MODBUS_SET_INT16_TO_INT8(test, 4, data[2]);
		rc = modbus_read_registers(ctx_rtu, 0, 2, data);
		if(-1 == rc)
		{
			perror("read registers error");
			device_close();
			pthread_mutex_unlock(&mutex);
			sleep(5);
			continue;
		}

		//光强无法获取，所以手动写入一个数据
		test[4] = 23;
		test[5] = 10;
		db_insert(test[0]*10+test[1]/10, test[2]*10+test[3]/10, test[4]*10+test[5]/10);//插入到数据库
		if(debug)
		{
			printf("humi: %d.%d temp:%d.%d light:%d.%d\r\n",test[0], test[1], test[2], test[3], test[4], test[5]);
		}

		//自动控制
		if(auto_switch)
		{
			auto_control(data);
		}

		device_close();
		pthread_mutex_unlock(&mutex);
		sleep(5);
	}
	return NULL;
}

static int server_accept(void)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	socklen_t addrlen;
	struct sockaddr_in clientAddr;
	addrlen = sizeof(clientAddr);
	memset(&clientAddr, 0, addrlen);
	int rc = -1;

	//接受连接请求
	master_scoket = accept(server_socket, (struct sockaddr *)&clientAddr, &addrlen);
	if(-1 == master_scoket)
	{
		perror("accept error");
		return -1;
	}
	if(debug)
		printf("New connection from %s: %d on socket %d\r\n",
				inet_ntoa(clientAddr.sin_addr), clientAddr.sin_port, master_scoket);

	//加入监听集合
	ev.events = EPOLLIN | EPOLLET;
	ev.data.fd = master_scoket;
	rc = epoll_ctl(epfd, EPOLL_CTL_ADD, master_scoket, &ev);
	if(-1 == rc)
	{
		perror("epoll add error");
		return -1;
	}

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;
}
static int device_open(void)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	int rc = -1;
	ctx_rtu = modbus_new_rtu(file_rtu, 9600, 'N', 8, 1);
	if(NULL == ctx_rtu)
	{
		perror("rtu new error");
		return -1;
	}

	if(debug)
		modbus_set_debug(ctx_rtu, 1);

	//设置超时时间
	rc = modbus_set_response_timeout(ctx_rtu, 5, 0);
	if(-1 == rc)
	{
		perror("timeout ser error");
		return -1;
	}

	//从机设备地址
	rc = modbus_set_slave(ctx_rtu, 1);
	if(-1 == rc)
	{
		perror("slave set error");
		return -1;
	}

	//连接
	rc = modbus_connect(ctx_rtu);
	if(-1 == rc)
	{
		perror("rtc connect error");
		return -1;
	}

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;
}
static int device_close(void)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	modbus_close(ctx_rtu);
	modbus_free(ctx_rtu);

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;
}
//响应客户端数据获取请求
static int server_get_data(const int startaddr, const int nb)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	int i;

	switch(startaddr)
	{
	case 0:
		db_seek(1, 1, 1);//查找湿度
		*(mb_mapping->tab_input_registers+0) = (uint16_t)atoi(pptable[1]);
		db_seek(2, 1, 1);//查找温度
		*(mb_mapping->tab_input_registers+1) = (uint16_t)atoi(pptable[1]);
		db_seek(3, 1, 1);//查找光照
		*(mb_mapping->tab_input_registers+2) = (uint16_t)atoi(pptable[1]);
		printf("humi:%d temp:%d light:%d",*(mb_mapping->tab_input_registers+0),
				*(mb_mapping->tab_input_registers+1), *(mb_mapping->tab_input_registers+2));
		break;
	case 1: //查找nb个湿度数据
	case 2://nb个温度
	case 3://nb个光强
		if(debug)
			printf("get date from database!(startaddr:%d nb:%d)\r\n", startaddr, nb);
		db_seek(startaddr, 1, nb);
		for(i=0; i<nb; i++)
		{
			if(debug)
				printf("%s\r\n", pptable[1+i]);
			*(mb_mapping->tab_input_registers+i+startaddr) = (uint16_t)atoi(pptable[i+1]);
		}
		break;
	default:
		printf("opt error!\r\n");
		return -1;
	}

	if(debug)
		printf("Leave %s\r\n", __func__);
	return 0;
}

//获取当前设备的状态
static int server_get_status(const int startaddr, const int nb)
{
	if(debug)
		printf("Enter %s\r\n", __func__);
	int rc = -1;

	pthread_mutex_lock(&mutex);
	rc = device_open();
	{
		device_close();
		pthread_mutex_unlock(&mutex);
		return -1;
	}

	switch(startaddr)
	{
	case 0: //喷淋（led1）
	case 1: //风扇
	case 2: //光照（led2）
	case 3: //蜂鸣器
		rc = modbus_read_bits(ctx_rtu, startaddr, nb, mb_mapping->tab_input_bits+startaddr);
		if(-1 == rc)
		{
			printf("read input bits error!\r\n");
			device_close();
			pthread_mutex_unlock(&mutex);
			return -1;
		}
	default:
		printf("指令请求错误\r\n");
		device_close();
		pthread_mutex_unlock(&mutex);
		return -1;
	}

	device_close();
	pthread_mutex_unlock(&mutex);

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;
}
//打开或关闭设备
static int server_set_status(const int startaddr, const int nb)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	pthread_mutex_lock(&mutex);
	device_open();
	int rc = 0;
	uint8_t query[] = {0x01, 0x05, 0x00, 0x00, 0x00, 0x00};
	query[3] = (uint8_t)startaddr;
	query[4] = *(mb_mapping->tab_bits+startaddr);
	printf("%d\r\n", *(mb_mapping->tab_bits+startaddr));

	switch(startaddr)
	{
	case 0: //喷淋（led1）
	case 1: //风扇
	case 2: //光照（led2）
	case 3: //蜂鸣器
		break;
	default:
		printf("opt error");
		device_close();
		pthread_mutex_unlock(&mutex);
		return -1;
	}
	rc = modbus_send_raw_request(ctx_rtu, query, 6);
	device_close();
	pthread_mutex_unlock(&mutex);

	if(debug)
		printf("Leave %s\r\n", __func__);

	return rc;
}

//处理请求
static int server_handle(void)
{
	if(debug)
		printf("Enter %s\r\n", __func__);

	uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH] = {0};
	int function = 0;
	int startaddr = 0;
	int nb = 0;
	int rc = 0;

	modbus_set_socket(ctx_tcp, master_scoket);
	rc = modbus_receive(ctx_tcp, query);
	if(-1 == rc)
	{
		fprintf(stderr, "receive error:%s\r\n", modbus_strerror(errno));
		close(master_scoket);
		epoll_ctl(epfd, EPOLL_CTL_DEL, master_scoket, &ev);
		return -1;
	}
	modbus_reply(ctx_tcp, query, rc, mb_mapping);//回复

	function = query[7];
	startaddr = MODBUS_GET_INT16_FROM_INT8(query, 8);
	nb = MODBUS_GET_INT16_FROM_INT8(query, 10);
	if(debug)
		printf("fucntion:%d, startaddr:%d, nb:%d\r\n", function, startaddr, nb);

	switch(function)
	{
	case MODBUS_FC_READ_COILS:
		if(debug)
			printf("读线圈!----------\r\n");
		//预留
		break;
	case MODBUS_FC_READ_DISCRETE_INPUTS: //获取设备状态
		if(debug)
			printf("读输入离散量!----------\r\n");
		server_get_status(startaddr, nb);
		break;
	case MODBUS_FC_READ_HOLDING_REGISTERS://获取设备默认设置
		if(debug)
			printf("读保持寄存器!----------\r\n");
		//库默认
		break;
	case MODBUS_FC_READ_INPUT_REGISTERS://获取数据
		if(debug)
			printf("读输入寄存器!----------\r\n");
		server_get_data(startaddr, nb);
		break;
	case MODBUS_FC_WRITE_SINGLE_COIL:
	case MODBUS_FC_WRITE_MULTIPLE_COILS://修改设备状态
		if(debug)
			printf("写线圈!---------\r\n");
		server_set_status(startaddr, nb);
		break;
	case MODBUS_FC_WRITE_SINGLE_REGISTER:
	case MODBUS_FC_WRITE_MULTIPLE_REGISTERS://修改默认设置
		if(debug)
			printf("写保持寄存器!---------\r\n");
		//默认
		break;
	default:
		if(debug)
			printf("输入的功能码有误!-------------\r\n");
	}

	if(debug)
		printf("Leave %s\r\n", __func__);

	return 0;
}


int main(void)
{
	server_build(); //服务器接口
	return 0;
}

